/**@<parser.c>::*/
#include <string.h>
/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock ';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt
 */

/** further simplifications for today only:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations stmtblock
 * 
 * declarations -> 
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    modules
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' block ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'block';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | ""
 */
#include <parser.h>
#include <keywords.h>
#include <tokens.h>

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  match(PROGRAM); 
  match(ID);
  match(';');
  block();
  match('.');
}

/* block -> declarations stmtblock*/
void block(void)
{declarations();stmtblock();}

#define MAXSYMLIST      0x10000
#define MAXIDSIZ        32
char symlist[MAXSYMLIST][MAXIDSIZ];
int sympos;

/* declarations -> 
*    [VAR idlist ':' type';' {idlist ':' type';'}]
*    modules*/
void declarations(void)
{
  if(lookahead==VAR){
    match(VAR);
rep_idlist:
    idlist();match(':');type();match(';');
    if(lookahead==ID)goto rep_idlist;
  }
  modules();
}

void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE)procedure();
    else function();
  }
}

void procedure(void)
{
  match(PROCEDURE);match(ID);formalparm();match(';');
  block();
  match(';');
}

void function(void)
{
  match(FUNCTION);match(ID);formalparm();match(':');type();
  match(';');
  block();
  match(';');
}

/* idlist -> ID {','ID}*/
void idlist(void)
{
  /** **/strcpy(symlist[sympos++],lexeme);/** **/
  match(ID);
  while(lookahead==','){
    match(',');
    /** **/strcpy(symlist[sympos++],lexeme);/** **/
    match(ID);
  }
}

extern symtab_insert(char const *, int);
void type(void)
{
  /** datatype **/int dtype,i;
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      break;
    default:
      match(dtype=REAL);
  }
  
  /** */
  for(i=0;i<sympos;i++) {
    if(symtab_lookup(symlist[i])) {
      fprintf(stderr,"symbol \"%s\" already declared\n",
        symlist[i]);
    }else{
      symtab_insert(symlist[i],dtype);
    }
  }
  /** */
  
}

void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}

void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}
void ifstmt(void)
{
  match(IF);expression();match(THEN);
    stmt();
  if(lookahead==ELSE) {
    match(ELSE);stmt();
  }
}
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}
void expression(void)
{
  /** an inherited attribute is get from the left side of
   * an assignment if any **/
  expr();
  if(isrelationalop(lookahead)){
    match(lookahead); expr();
  }
  /** at this point we get a resulting data type as a
   * synthesized attribute **/
}
void expr(void)
{
  if(lookahead=='-'){
    match('-');
  }
  term();
  while(isaddop(lookahead)){
    match(lookahead); term();
  }
}
void term(void)
{
  factor();
  while(ismulop(lookahead)){
    match(lookahead); factor();
  }
}
void factor(void){
    switch(lookahead){
      case UINT:
        /** **/
        acctype = max(acctype, 1);
        match(UINT);break;
      case FLOAT:
        /** **/
        acctype = max(acctype, 2);
        match(FLOAT);break;
      case ID:
        /** in this context the symbol must be a variable or
         * a function using a symtab_lookup call **/
        /** **/
        if((symbol_entry = symtab_lookup(lexeme))==0){
          fprintf(stderr,"semantic: symbol not found");
        }else{
          switch(symtab[symbol_entry][1]){
            case 1:/** this is a variable **/
            case 2:/** this is a function call **/
              break;
            default:
              fprintf(stderr,"symbol in ilegal context");
          }
        }
        strcpy(symbol, lexeme);
        /** **/
        match(ID);
        if(lookahead=='('){
          /** this is the context for function call **/
          match('(');exprlist();match(')');
          /** subroutine call is here **/
        }else{
          /** this is the context for simple variable **/
          /** **/
          offset=symtab[symbol_entry][3];
          if(offset==0){
            /** the variable is global, then its identification
             * itself is the variable address **/
            /** **/
            fprintf(target,"\tmov %s,%%eax\n",symbol);
            /** **/
          }else{
            /** in this context all symbols are looked up in the
             symbol table in order to compose the stack address:
                <offset>(%ebp)
             for instance, a variable at offset -4 would be
               written as
                -4(%ebp)
               a second variable with size 4 would be translated
               to
                -8(%ebp)
               on the other hand, a parameter in the first slot
               would be interpreted as
                8(%ebp)
            **/
            /** **/
            fprintf(target,"\tmov %d(%%ebp),%%eax\n",offset);
            /** **/
          }
        }
        break;
      case '(':
        match('(');
        /** **/returnedtype=0;/** **/
        expression();
        /** **/acctype = max(acctype, returnedtype);/** **/
        match(')');
        /** at this point, expression has synthesized an
         * attribute storing the resulting data type from the
         * expression. however, the just calculated attribute
         * might be greater than the l-attribute along the
         * current expression, so that a promotion must occur
         **/
        
        break;
      default:
        match(NOT);factor();
    }
}
void exprlist(void)
{
  /** this is the context for passing parameters to procedures
   * or functions */
  expression();
  /** expression result stored in accumulator **/
  /** push the accumulator onto the stack **/
  /** **/fprintf(target,"\tpush %%eax\n");/** **/
  while(lookahead==','){
    match(',');
    expression();
    /** expression result stored in accumulator **/
    /** push the accumulator onto the stack **/
    /** **/fprintf(target,"\tpush %%eax\n");/** **/
  }
}
